package com.cencat.common.utils;

import com.cencat.common.constants.CommonConstants;
import com.cencat.common.enums.ErrorCode;
import com.cencat.common.exception.BusinessException;
import com.cencat.common.utils.CencatStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.UUID;

/**
 * 文件工具类
 * 提供文件上传、下载、删除等操作
 * 
 * @author cencat
 * @since 2024-01-01
 */

public class FileUtils {

    private static final Logger log = LoggerFactory.getLogger(FileUtils.class);

    /**
     * 私有构造函数，防止实例化
     */
    private FileUtils() {
    }

    /**
     * 上传文件
     * 
     * @param file     上传的文件
     * @param uploadDir 上传目录
     * @return 文件相对路径
     * @throws BusinessException 业务异常
     */
    public static String uploadFile(MultipartFile file, String uploadDir) throws BusinessException {
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ErrorCode.FILE_UPLOAD_FAILED, "文件不能为空");
        }

        // 检查文件大小
        if (file.getSize() > CommonConstants.File.MAX_FILE_SIZE) {
            throw new BusinessException(ErrorCode.FILE_SIZE_EXCEEDED, "文件大小不能超过10MB");
        }

        // 获取原始文件名
        String originalFilename = file.getOriginalFilename();
        if (CencatStringUtils.isBlank(originalFilename)) {
            throw new BusinessException(ErrorCode.FILE_UPLOAD_FAILED, "文件名不能为空");
        }

        // 获取文件扩展名
        String extension = getFileExtension(originalFilename);
        if (CencatStringUtils.isBlank(extension)) {
            throw new BusinessException(ErrorCode.FILE_FORMAT_ERROR, "文件格式不正确");
        }

        // 检查文件格式
        validateFileFormat(extension);

        // 生成新的文件名
        String newFilename = generateFilename(extension);

        // 创建上传目录
        String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        String fullUploadDir = uploadDir + File.separator + datePath;
        createDirectories(fullUploadDir);

        // 保存文件
        String filePath = fullUploadDir + File.separator + newFilename;
        try {
            file.transferTo(new File(filePath));
            log.info("文件上传成功: originalFilename={}, newFilename={}, filePath={}", originalFilename, newFilename, filePath);
            return datePath + "/" + newFilename;
        } catch (IOException e) {
            log.error("文件上传失败: {}", e.getMessage(), e);
            throw new BusinessException(ErrorCode.FILE_UPLOAD_FAILED, "文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 上传图片文件
     * 
     * @param file 上传的图片文件
     * @return 图片相对路径
     * @throws BusinessException 业务异常
     */
    public static String uploadImage(MultipartFile file) throws BusinessException {
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ErrorCode.FILE_UPLOAD_FAILED, "图片文件不能为空");
        }

        // 检查图片大小
        if (file.getSize() > CommonConstants.File.MAX_IMAGE_SIZE) {
            throw new BusinessException(ErrorCode.FILE_SIZE_EXCEEDED, "图片大小不能超过5MB");
        }

        // 获取文件扩展名
        String originalFilename = file.getOriginalFilename();
        String extension = getFileExtension(originalFilename);
        
        // 检查图片格式
        if (!isImageFormat(extension)) {
            throw new BusinessException(ErrorCode.FILE_FORMAT_ERROR, "不支持的图片格式");
        }

        return uploadFile(file, CommonConstants.File.IMAGE_UPLOAD_PATH);
    }

    /**
     * 删除文件
     * 
     * @param filePath 文件路径
     * @return 是否删除成功
     */
    public static boolean deleteFile(String filePath) {
        if (CencatStringUtils.isBlank(filePath)) {
            return false;
        }

        try {
            Path path = Paths.get(filePath);
            boolean deleted = Files.deleteIfExists(path);
            if (deleted) {
                log.info("文件删除成功: {}", filePath);
            } else {
                log.warn("文件不存在或删除失败: {}", filePath);
            }
            return deleted;
        } catch (IOException e) {
            log.error("文件删除失败: filePath={}, error={}", filePath, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 检查文件是否存在
     * 
     * @param filePath 文件路径
     * @return 是否存在
     */
    public static boolean fileExists(String filePath) {
        if (CencatStringUtils.isBlank(filePath)) {
            return false;
        }
        return Files.exists(Paths.get(filePath));
    }

    /**
     * 获取文件大小
     * 
     * @param filePath 文件路径
     * @return 文件大小（字节）
     */
    public static long getFileSize(String filePath) {
        if (CencatStringUtils.isBlank(filePath)) {
            return 0;
        }

        try {
            return Files.size(Paths.get(filePath));
        } catch (IOException e) {
            log.error("获取文件大小失败: filePath={}, error={}", filePath, e.getMessage());
            return 0;
        }
    }

    /**
     * 读取文件内容
     * 
     * @param filePath 文件路径
     * @return 文件内容
     */
    public static String readFileContent(String filePath) {
        if (CencatStringUtils.isBlank(filePath)) {
            return null;
        }

        try {
            return new String(Files.readAllBytes(Paths.get(filePath)), CommonConstants.System.CHARSET_UTF8);
        } catch (IOException e) {
            log.error("读取文件内容失败: filePath={}, error={}", filePath, e.getMessage());
            return null;
        }
    }

    /**
     * 读取文件内容为字符串
     * 
     * @param filePath 文件路径
     * @return 文件内容字符串
     */
    public static String readFileToString(String filePath) {
        return readFileContent(filePath);
    }

    /**
     * 写入文件内容
     * 
     * @param filePath 文件路径
     * @param content  文件内容
     * @return 是否写入成功
     */
    public static boolean writeFileContent(String filePath, String content) {
        if (CencatStringUtils.isBlank(filePath) || content == null) {
            return false;
        }

        try {
            Path path = Paths.get(filePath);
            // 创建父目录
            createDirectories(path.getParent().toString());
            Files.write(path, content.getBytes(CommonConstants.System.CHARSET_UTF8));
            log.info("文件写入成功: {}", filePath);
            return true;
        } catch (IOException e) {
            log.error("文件写入失败: filePath={}, error={}", filePath, e.getMessage());
            return false;
        }
    }

    /**
     * 复制文件
     * 
     * @param sourcePath 源文件路径
     * @param targetPath 目标文件路径
     * @return 是否复制成功
     */
    public static boolean copyFile(String sourcePath, String targetPath) {
        if (CencatStringUtils.isBlank(sourcePath) || CencatStringUtils.isBlank(targetPath)) {
            return false;
        }

        try {
            Path source = Paths.get(sourcePath);
            Path target = Paths.get(targetPath);
            
            // 创建目标目录
            createDirectories(target.getParent().toString());
            
            Files.copy(source, target);
            log.info("文件复制成功: {} -> {}", sourcePath, targetPath);
            return true;
        } catch (IOException e) {
            log.error("文件复制失败: sourcePath={}, targetPath={}, error={}", sourcePath, targetPath, e.getMessage());
            return false;
        }
    }

    /**
     * 移动文件
     * 
     * @param sourcePath 源文件路径
     * @param targetPath 目标文件路径
     * @return 是否移动成功
     */
    public static boolean moveFile(String sourcePath, String targetPath) {
        if (CencatStringUtils.isBlank(sourcePath) || CencatStringUtils.isBlank(targetPath)) {
            return false;
        }

        try {
            Path source = Paths.get(sourcePath);
            Path target = Paths.get(targetPath);
            
            // 创建目标目录
            createDirectories(target.getParent().toString());
            
            Files.move(source, target);
            log.info("文件移动成功: {} -> {}", sourcePath, targetPath);
            return true;
        } catch (IOException e) {
            log.error("文件移动失败: sourcePath={}, targetPath={}, error={}", sourcePath, targetPath, e.getMessage());
            return false;
        }
    }

    /**
     * 创建目录
     * 
     * @param dirPath 目录路径
     * @return 是否创建成功
     */
    public static boolean createDirectories(String dirPath) {
        if (CencatStringUtils.isBlank(dirPath)) {
            return false;
        }

        try {
            Path path = Paths.get(dirPath);
            if (!Files.exists(path)) {
                Files.createDirectories(path);
                log.debug("目录创建成功: {}", dirPath);
            }
            return true;
        } catch (IOException e) {
            log.error("目录创建失败: dirPath={}, error={}", dirPath, e.getMessage());
            return false;
        }
    }

    /**
     * 获取文件扩展名
     * 
     * @param filename 文件名
     * @return 扩展名（不包含点号）
     */
    public static String getFileExtension(String filename) {
        if (CencatStringUtils.isBlank(filename)) {
            return "";
        }

        int lastDotIndex = filename.lastIndexOf('.');
        if (lastDotIndex == -1 || lastDotIndex == filename.length() - 1) {
            return "";
        }

        return filename.substring(lastDotIndex + 1).toLowerCase();
    }

    /**
     * 获取文件名（不包含扩展名）
     * 
     * @param filename 文件名
     * @return 文件名（不包含扩展名）
     */
    public static String getFileNameWithoutExtension(String filename) {
        if (CencatStringUtils.isBlank(filename)) {
            return "";
        }

        int lastDotIndex = filename.lastIndexOf('.');
        if (lastDotIndex == -1) {
            return filename;
        }

        return filename.substring(0, lastDotIndex);
    }

    /**
     * 生成唯一文件名
     * 
     * @param extension 文件扩展名
     * @return 唯一文件名
     */
    public static String generateFilename(String extension) {
        String uuid = UUID.randomUUID().toString().replace("-", "");
        String timestamp = String.valueOf(System.currentTimeMillis());
        return uuid + "_" + timestamp + "." + extension;
    }

    /**
     * 格式化文件大小
     * 
     * @param size 文件大小（字节）
     * @return 格式化后的文件大小
     */
    public static String formatFileSize(long size) {
        if (size < 1024) {
            return size + " B";
        } else if (size < 1024 * 1024) {
            return String.format("%.2f KB", size / 1024.0);
        } else if (size < 1024 * 1024 * 1024) {
            return String.format("%.2f MB", size / (1024.0 * 1024.0));
        } else {
            return String.format("%.2f GB", size / (1024.0 * 1024.0 * 1024.0));
        }
    }

    /**
     * 检查是否为图片格式
     * 
     * @param extension 文件扩展名
     * @return 是否为图片格式
     */
    public static boolean isImageFormat(String extension) {
        if (CencatStringUtils.isBlank(extension)) {
            return false;
        }
        return Arrays.asList(CommonConstants.File.ALLOWED_IMAGE_FORMATS).contains(extension.toLowerCase());
    }

    /**
     * 检查是否为文档格式
     * 
     * @param extension 文件扩展名
     * @return 是否为文档格式
     */
    public static boolean isDocumentFormat(String extension) {
        if (CencatStringUtils.isBlank(extension)) {
            return false;
        }
        return Arrays.asList(CommonConstants.File.ALLOWED_DOCUMENT_FORMATS).contains(extension.toLowerCase());
    }

    /**
     * 验证文件格式
     * 
     * @param extension 文件扩展名
     * @throws BusinessException 业务异常
     */
    private static void validateFileFormat(String extension) throws BusinessException {
        if (!isImageFormat(extension) && !isDocumentFormat(extension)) {
            throw new BusinessException(ErrorCode.FILE_FORMAT_ERROR, "不支持的文件格式: " + extension);
        }
    }

    /**
     * 获取MIME类型
     * 
     * @param extension 文件扩展名
     * @return MIME类型
     */
    public static String getMimeType(String extension) {
        if (CencatStringUtils.isBlank(extension)) {
            return "application/octet-stream";
        }

        switch (extension.toLowerCase()) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "gif":
                return "image/gif";
            case "bmp":
                return "image/bmp";
            case "webp":
                return "image/webp";
            case "pdf":
                return "application/pdf";
            case "doc":
                return "application/msword";
            case "docx":
                return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            case "xls":
                return "application/vnd.ms-excel";
            case "xlsx":
                return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case "ppt":
                return "application/vnd.ms-powerpoint";
            case "pptx":
                return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
            case "txt":
                return "text/plain";
            default:
                return "application/octet-stream";
        }
    }

    /**
     * 清理临时文件
     * 
     * @param tempDir 临时目录
     * @param maxAge  最大存活时间（毫秒）
     */
    public static void cleanTempFiles(String tempDir, long maxAge) {
        if (CencatStringUtils.isBlank(tempDir)) {
            return;
        }

        try {
            Path tempPath = Paths.get(tempDir);
            if (!Files.exists(tempPath)) {
                return;
            }

            long currentTime = System.currentTimeMillis();
            Files.walk(tempPath)
                    .filter(Files::isRegularFile)
                    .filter(path -> {
                        try {
                            long lastModified = Files.getLastModifiedTime(path).toMillis();
                            return currentTime - lastModified > maxAge;
                        } catch (IOException e) {
                            log.error("获取文件修改时间失败: {}", path, e);
                            return false;
                        }
                    })
                    .forEach(path -> {
                        try {
                            Files.delete(path);
                            log.debug("清理临时文件: {}", path);
                        } catch (IOException e) {
                            log.error("删除临时文件失败: {}", path, e);
                        }
                    });
        } catch (IOException e) {
            log.error("清理临时文件失败: tempDir={}, error={}", tempDir, e.getMessage());
        }
    }
}